package gloader

import (
	"fmt"
	"go.uber.org/zap"
	"strings"
	"sync"
	"wzgames/internal/app"
	"wzgames/internal/constant"
	"wzgames/internal/model"
	"wzgames/internal/services/idc"
	"wzgames/pkg/common/component"
	"wzgames/pkg/components/mysql"
	"wzgames/pkg/config"
	"wzgames/pkg/logger"
)

// 数据库存储连接实例

type DBProxy struct {
	mu   sync.RWMutex
	wdbc map[string]*component.ComponentConfig // 配置
	wdb  map[string]*mysql.DS                  // 实例

	idw        *idc.IdWorker // 基于数据库的ID生成实例
	idwStarted bool
}

func newDBProxy() *DBProxy {
	d := &DBProxy{
		wdb:  make(map[string]*mysql.DS),
		wdbc: make(map[string]*component.ComponentConfig),
		idw:  idc.NewIdWorker(),
	}
	return d
}

// InitWdb 初始化boot配置的数据库配置
func (d *DBProxy) InitWdb(c *model.DBConfig) error {
	if c == nil {
		return fmt.Errorf("invalid wdb config")
	}
	d.mu.Lock()
	for _, dbc := range c.ProxyConfigs {
		d.wdbc[dbc.Name] = &component.ComponentConfig{
			ID:       mysql.ID(),
			Name:     dbc.Name,
			Config:   d.wdbConfig(dbc),
			Dateline: dbc.Dateline,
		}
	}
	d.mu.Unlock()

	return nil
}

// StartIdWorker 启动基于数据库的ID生成
func (d *DBProxy) StartIdWorker() error {
	gmds := d.GetProxy(constant.MysqlOfIDC)
	if gmds == nil {
		return fmt.Errorf("idc db create fail")
	}
	d.idw.SetDao(gmds)
	d.idwStarted = true
	return nil
}

// wdbConfig 目前只MySQL的连接
func (d *DBProxy) wdbConfig(dbc *model.DBC) config.ConfData {
	c := &config.YAMLConfData{}
	c.ReplaceData(make(map[string]interface{}))
	c.SetRawValue("driverName", dbc.DriverName)
	var dataSourceName strings.Builder // root:admin888@tcp(localhost:3306)/wz_gm
	dataSourceName.WriteString(dbc.User)
	dataSourceName.WriteString(":")
	dataSourceName.WriteString(dbc.Password)
	dataSourceName.WriteString("@tcp(")
	dataSourceName.WriteString(dbc.Host)
	dataSourceName.WriteString(":")
	dataSourceName.WriteString(dbc.Port)
	dataSourceName.WriteString(")/")
	dataSourceName.WriteString(dbc.SchemaName)
	dataSourceName.WriteString("?charset=utf8mb4&parseTime=true")
	c.SetRawValue("dataSourceName", dataSourceName.String())
	c.SetRawValue("maxOpenConns", dbc.MaxOpenConns)
	c.SetRawValue("maxIdleConns", dbc.MaxIdleConns)
	c.SetRawValue("connMaxLifetime", dbc.ConnMaxLifetime)
	return c
}

// UpdateWdb 动态更新数据库连接实例
func (d *DBProxy) UpdateWdb(c *model.DBConfig) {
	if c == nil {
		return
	}
	d.mu.Lock()
	defer d.mu.Unlock()
	for _, dbc := range c.ProxyConfigs {
		if oldC, ok := d.wdbc[dbc.Name]; ok {
			if oldC.Dateline < dbc.Dateline {
				oldC.Dateline = dbc.Dateline
				oldC.Update(d.wdbConfig(dbc))
				if _, ok1 := d.wdb[dbc.Name]; ok1 {
					newInst, err := app.Comp().UpdateInstance(dbc.Name, oldC)
					if err != nil {
						logger.Error("[DB] update mysql inst fail", zap.String("Name", dbc.Name), zap.Error(err))
					} else {
						d.wdb[dbc.Name] = newInst.(*mysql.DS)
						if dbc.Name == constant.MysqlOfIDC && d.idwStarted {
							d.idw.SetDao(d.wdb[dbc.Name])
						}
					}
				}
			}
		} else {
			d.wdbc[dbc.Name] = &component.ComponentConfig{
				ID:     mysql.ID(),
				Name:   dbc.Name,
				Config: d.wdbConfig(dbc),
			}
		}
	}
}

func (d *DBProxy) GetProxy(name string) *mysql.DS {
	d.mu.RLock()
	if dbc, ok := d.wdbc[name]; ok {
		if ds, ok1 := d.wdb[dbc.Name]; ok1 {
			d.mu.RUnlock()
			return ds
		}
		d.mu.RUnlock()

		d.mu.Lock()
		if ds, ok1 := d.wdb[dbc.Name]; ok1 {
			d.mu.Unlock()
			return ds
		}
		inst, err := app.Comp().CreateInstance(dbc)
		if err != nil {
			d.mu.Unlock()
			logger.Error("[DB] create mysql inst fail", zap.String("Name", dbc.Name), zap.Error(err))
			return nil
		}
		ds := inst.(*mysql.DS)
		d.wdb[dbc.Name] = ds
		d.mu.Unlock()
		return ds
	}
	d.mu.RUnlock()

	return nil
}

func (d *DBProxy) GenID(bizid int64, step uint64) uint64 {
	return d.idw.ID(bizid, step)
}
